package com.atguigu.tingshu.common.login;

import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.user.UserInfo;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.SneakyThrows;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/**
 * @author atguigu-mqx
 * @ClassName GuiGuLoginAspect
 * @description: TODO
 * @date 2023年12月22日
 * @version: 1.0
 */
@Aspect
@Component
public class GuiGuLoginAspect {

    @Autowired
    private RedisTemplate redisTemplate;

    //  环绕通知： 1、自定义异常 2、多线程异步， 3、熔断
    //  @Around("@annotation(org.springframework.transaction.annotation.Transactional)")
    //  @Around("@annotation(com.atguigu.tingshu.common.login.GuiGuLogin)")
    //  GuiGuLogin 表示是否需要登录:
    @SneakyThrows
    @Around("execution(* com.atguigu.tingshu.*.api.*.*(..)) && @annotation(guiGuLogin)")
    public Object loginAspect(ProceedingJoinPoint point,GuiGuLogin guiGuLogin){
        //  声明一个对象
        Object object = new Object();
        //  必须要做验证：是否真正登录！ --> {登录的业务处理： 如果登录成功之后，我们会返回一个token，放入请求头中.同时将用户信息存储到缓存中}
        //  需要在请求头中获取token. 通过 token 来获取缓存的信息.
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
        //  获取请求对象
        HttpServletRequest request = servletRequestAttributes.getRequest();
        //  获取响应对象
        //  HttpServletResponse response = servletRequestAttributes.getResponse();
        String token = request.getHeader("token");
        //  判断
        if (guiGuLogin.required()){
            //  必须要登录
            //  判断token
            if (StringUtils.isEmpty(token)){
                //  token直接抛出异常.
                throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
            } else {
                //  token 不为空，可以获取缓存中的用户信息，如果用户信息是空，抛出异常.
                //  UserInfo userInfo = service.login();  userInfo 存储到缓存. hash---JavaObject hset key field=property value;
                //  userInfo 如果没有修改，完全可以存储字符串. 512m String  set key value;
                //  key = 不能重复，唯一! key=token value=UserInfo.toString();
                String loginKey = RedisConstant.USER_LOGIN_KEY_PREFIX+token;
                //  获取缓存中的数据
                UserInfo userInfo = (UserInfo) redisTemplate.opsForValue().get(loginKey);
                if (null == userInfo){
                    throw new GuiguException(ResultCodeEnum.LOGIN_AUTH);
                }
            }
        }
        //  虽然不需要登录，但是可能需要用户Id
        if (!StringUtils.isEmpty(token)){
            //  获取用户数据.
            String loginKey = RedisConstant.USER_LOGIN_KEY_PREFIX+token;
            //  获取缓存中的数据
            UserInfo userInfo = (UserInfo) redisTemplate.opsForValue().get(loginKey);
            //  判断
            if (null != userInfo){
                //  存储用户Id
                AuthContextHolder.setUserId(userInfo.getId());
                //  存储用户昵称
                AuthContextHolder.setUsername(userInfo.getNickname());
            }
        }
        //  返回数据:
        //  point.proceed() == 执行目标方法: 表示执行方法体
        return point.proceed();
    }
}
